package com.frejoys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.frejoys.common.dto.admin.*;
import com.frejoys.common.dto.app.CategoryPidDto;
import com.frejoys.common.dto.app.CategorySiteDto;
import com.frejoys.common.dto.common.IdDto;
import com.frejoys.common.dto.store.CategoryListDto;
import com.frejoys.common.enums.*;
import com.frejoys.common.error.HttpStatus;
import com.frejoys.common.exception.GeneralException;
import com.frejoys.common.util.AppConfigUtil;
import com.frejoys.common.util.CommonUtil;
import com.frejoys.common.vo.admin.AdminCategoryInfoVo;
import com.frejoys.common.vo.admin.AdminCategoryPageVo;
import com.frejoys.common.vo.admin.CategorySelectVo;
import com.frejoys.common.vo.app.CategoryListVo;
import com.frejoys.common.vo.app.CategorySonListVo;
import com.frejoys.dao.entity.AgentRoleGroup;
import com.frejoys.dao.entity.Category;
import com.frejoys.dao.entity.Store;
import com.frejoys.dao.mapper.CategoryMapper;
import com.frejoys.dao.mapper.GoodsMapper;
import com.frejoys.dao.mapper.StoreMapper;
import com.frejoys.service.CategoryService;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;


// 品类
@Service
@RequiredArgsConstructor
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {

    private final StoreMapper storeMapper;

    private final GoodsMapper goodsMapper;

    @Override
    @Cacheable(cacheNames = "category", key = "#root.method.name", unless = "#result.size() == 0")
    public List<Category> listInfo() {
        LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Category::getStatus, EStatus.on.getValue());
        wrapper.eq(Category::getIsShow, EShowStatus.show.getValue());
        return list(wrapper);
    }

    @Override
    @Cacheable(cacheNames = "category", key = "#root.method.name", unless = "#result.size() == 0")
    public List<Category> listAll() {
        return list();
    }

    @Override
    public List<CategorySelectVo> getSelectVo(List<Category> list, CategoryListDto dto) {
        // 若指定了父id,只指定的品类及子品类
        Integer categoryId = dto.getCategoryId();
        if (categoryId != null && !categoryId.equals(0)) {
            list = list.stream().filter(c -> {
                if (StrUtil.isEmpty(c.getPath())) {
                    return false;
                }
                List<Integer> collect = Arrays.stream(c.getPath().split(",")).map(Integer::parseInt).toList();
                return c.getId().equals(categoryId) || collect.contains(categoryId);
            }).collect(Collectors.toList());
        }
        // 设置名称前缀
        setNamePrefix(list);
        int asInt = list.stream().mapToInt(c -> {
            return c.getPath().split(",").length;
        }).min().orElse(0);

        // 最高级品类
        List<Category> topLevelList = list.stream().filter(c -> {
            return c.getPath().split(",").length == asInt;
        }).collect(Collectors.toList());
        List<Category> result = new ArrayList<>();
        // 排序品类
        sortCategory(topLevelList);
        // 根据最高级品类,组装子类
        fillData(list, topLevelList, result);
        List<CategorySelectVo> selectVos = new ArrayList<>();
        for (Category category : result) {
            selectVos.add(getCategorySelectVo(category.getName(), category.getId()));
//            System.out.println(category.getName());
        }
        return selectVos;

    }

    /**
     * 设置品类前缀
     * 返回示例:
     * └ 总部运营
     * ├ 事业部
     * │ └ 城市总
     * │   └ 总监
     * │     └ 推手
     * ├ 文案部
     * └ 财务部
     */
    public void setNamePrefix(List<Category> categoryList) {

        for (Category c : categoryList) {
            int length = c.getPath().split(",").length;
            if (length == 1) {
                c.setName(" └ " + c.getName());
            } else if (length > 1) {
                if (length == 2) {
                    c.setName("   ├ " + c.getName());
                } else {
                    StringBuilder blank = new StringBuilder();
                    if (length > 3) {
                        blank = new StringBuilder("  ");
                        blank.append(" ".repeat(length - 3));
                    }
                    c.setName("   │ " + blank + "└ " + c.getName());
                }
            }
        }
    }

    /**
     * 根据指定的分类,组装对应子类,并按照层级及sort字段排序
     *
     * @param list    所有分类数据
     * @param topList 指定的分类列表
     * @param result  排序后的指定的分类数据
     */
    public void fillData(List<Category> list, List<Category> topList, List<Category> result) {

        if (!CollectionUtils.isEmpty(topList)) {
            if (CollectionUtils.isEmpty(result)) {
                result.addAll(topList);
            }
            for (Category category : topList) {
                int index = result.indexOf(category);
                // 计算出子元素的path,用于后续根据path筛选
                List<Integer> neededSubPath =
                        Arrays.stream(category.getPath().split(","))
                                .mapToInt(Integer::parseInt)
                                .boxed()
                                .collect(Collectors.toList());
                neededSubPath.add(category.getId());

                // 过滤出当前循环品类的子类
                List<Category> subEl = list.stream().filter(c -> {
                    // 将path转换成List
                    List<Integer> elPath = Arrays.stream(c.getPath().split(","))
                            .mapToInt(Integer::parseInt)
                            .boxed()
                            .toList();
                    return elPath.equals(neededSubPath);
                }).collect(Collectors.toList());
                // Collectors.toCollection(ArrayList::new)
                if (!subEl.isEmpty()) {
                    // 排序子类
                    sortCategory(subEl);
                    // 将子类插入到当前品类后面
                    result.addAll(index + 1, subEl);
                    // 如果子类还存在子类,递归
                    int length = subEl.get(0).getPath().split(",").length;
                    List<Category> collect = list.stream().filter(c -> {
                        return c.getPath().split(",").length == length + 1;
                    }).toList();
                    if (!collect.isEmpty()) {
                        fillData(list, subEl, result);
                    }
                }

            }
        }
    }


    /**
     * 品类排序,根据层级和sort字段
     */
    public void sortCategory(List<Category> list) {
        list.sort(new Comparator<>() {
            @Override
            public int compare(Category o1, Category o2) {
                int result = o1.getPath().split(",").length - o2.getPath().split(",").length;
                // 如果级别相同，按照 sort 字段升序排序
                if (result == 0) {
                    result = Integer.compare(o1.getSort(), o2.getSort());
                }
                return result;
            }
        });
    }

    public List<CategoryListVo> onCategory(CategorySiteDto dto, List<Category> categoryList) {
        List<CategoryListVo> list = new ArrayList<>();

        if (categoryList.isEmpty()) {
            return list;
        }

        for (Category category : categoryList) {

            if (StrUtil.isNotEmpty(category.getSite())) {

                if (category.getSite().contains(dto.getSite().toString())) {
                    CategoryListVo vo = new CategoryListVo();
                    vo.setId(category.getId());
                    vo.setName(category.getName());
                    vo.setCover(AppConfigUtil.getUrlPrefix(category.getCover()));
                    list.add(vo);
                }
            }
        }
        return list;
    }


    public List<CategoryListVo> categoryListInfo(CategoryPidDto dto, List<Category> categoryList) {
        List<CategoryListVo> list = new ArrayList<>();

        CategoryListVo selfVo = null;

        for (Category category : categoryList) {
            if (StrUtil.isNotEmpty(category.getSite()) && StrUtil.isNotEmpty(category.getPath())) {
                List<String> pathList = Arrays.asList(category.getPath().split(","));

                if (category.getSite().contains(dto.getSite().toString()) && pathList.contains(dto.getId().toString())) {
                    CategoryListVo vo = new CategoryListVo();
                    vo.setId(category.getId());
                    vo.setName(category.getName());
                    vo.setCover(AppConfigUtil.getUrlPrefix(category.getCover()));
                    list.add(vo);
                }
            }
            if (selfVo == null && category.getSite().contains(dto.getSite().toString()) && category.getId() == dto.getId()) {
                selfVo = new CategoryListVo();
                selfVo.setId(category.getId());
                selfVo.setName(category.getName());
                selfVo.setCover(AppConfigUtil.getUrlPrefix(category.getCover()));
            }
        }

        if (selfVo != null) {
            list.add(selfVo);
        }

        return list;
    }


    public List<CategoryListVo> sonListInfo(CategoryPidDto dto, List<Category> categoryList) {
        List<CategoryListVo> list = new ArrayList<>();

        for (Category category : categoryList) {
            if (StrUtil.isNotEmpty(category.getSite()) && StrUtil.isNotEmpty(category.getPath())) {
                List<String> pathList = Arrays.asList(category.getPath().split(","));

                if (category.getSite().contains(dto.getSite().toString()) && pathList.contains(dto.getId().toString())) {
                    CategoryListVo vo = new CategoryListVo();
                    vo.setId(category.getId());
                    vo.setName(category.getName());
                    vo.setCover(AppConfigUtil.getUrlPrefix(category.getCover()));
                    list.add(vo);
                }
            }
        }
        return list;
    }


    public List<CategorySonListVo> categoryAndSon(CategorySiteDto dto, List<Category> categories) {
        List<Category> categoryLists = new ArrayList<>();
        List<CategorySonListVo> list = new ArrayList<>();

        if (categories.isEmpty()) {
            return list;
        }
        //取出对应位置的数据
        for (Category category : categories) {
            if (StrUtil.isNotEmpty(category.getSite())) {
                if (category.getSite().contains(dto.getSite().toString())) {
                    categoryLists.add(category);
                }
            }
        }

        if (!categoryLists.isEmpty()) {
            //分组
            Map<Integer, List<Category>> map = categoryLists.stream().collect(Collectors.groupingBy(Category::getPid));
            List<Category> categoryList = new ArrayList<>();

            if (map.containsKey(0)) {
                categoryList = map.get(0);
            }

            if (!categoryList.isEmpty()) {

                for (Category category : categoryList) {

                    List<CategoryListVo> categoryListVos = new ArrayList<>();
                    CategorySonListVo vo = new CategorySonListVo();
                    vo.setId(category.getId());
                    vo.setName(category.getName());
                    vo.setCover(AppConfigUtil.getUrlPrefix(category.getCover()));
                    vo.setCategoryListVos(categoryListVos);

                    if (map.containsKey(category.getId())) {
                        //取 子级品类
                        List<Category> categorySon = map.get(category.getId());

                        categorySon.forEach(son -> {
                            CategoryListVo categoryListVo = new CategoryListVo();
                            categoryListVo.setId(son.getId());
                            categoryListVo.setName(son.getName());
                            categoryListVo.setCover(AppConfigUtil.getUrlPrefix(son.getCover()));

                            categoryListVos.add(categoryListVo);
                        });
                    }
                    list.add(vo);
                }
                ;
            }
        }
        return list;
    }


    @Override
    @CacheEvict(cacheNames = "category", allEntries = true)
    public boolean addInfo(CategoryAddDto dto) {
        Category category = BeanUtil.copyProperties(dto, Category.class);

        //查询出上级层级
        Category categoryPath = baseMapper.getPidPath(dto.getPid());
        category.setPath(ObjectUtil.isEmpty(categoryPath) ? "0" : CommonUtil.getPath(categoryPath.getId(), categoryPath.getPath()));
        category.setStatus(ECategoryStatus.valid.getValue());
        //默认不显示
        category.setIsShow(EShowStatus.show.getValue());
        return save(category);
    }

    @Override
    @CacheEvict(cacheNames = "category", allEntries = true)
    public boolean upInfo(CategoryUpDto dto) {

        LambdaUpdateWrapper<Category> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(Category::getId, dto.getId());
        if (StrUtil.isNotEmpty(dto.getName())) {
            wrapper.set(Category::getName, dto.getName());
        }
        if (StrUtil.isNotEmpty(dto.getSite())) {
            wrapper.set(Category::getSite, dto.getSite());
        }

        if (StrUtil.isNotEmpty(dto.getCover())) {
            wrapper.set(Category::getCover, dto.getCover());
        }
        if (dto.getIsShow() != null) {
            wrapper.set(Category::getIsShow, dto.getIsShow());
        }
        if (dto.getPid() != null) {
            wrapper.set(Category::getPid, dto.getPid());

            //查询出上级层级
            Category categoryPath = baseMapper.getPidPath(dto.getPid());
            wrapper.set(Category::getPath,ObjectUtil.isEmpty(categoryPath) ? "0" : CommonUtil.getPath(categoryPath.getId(), categoryPath.getPath()));
        }
        if (dto.getSort() != null) {
            wrapper.set(Category::getSort, dto.getSort());
        }
        return update(wrapper);
    }

    @Override
    @CacheEvict(cacheNames = "category", allEntries = true)
    public boolean delInfo(DelIdDto dto) {
        LambdaUpdateWrapper<Category> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(Category::getId, dto.getId());
        wrapper.set(Category::getStatus,dto.getStatus());
        return update(wrapper);
    }

    @Override
    public Map<Integer, String> getIdNameMap() {
        LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(Category::getId, Category::getName);
        wrapper.eq(Category::getStatus, EStatus.on.getValue());
        List<Category> categories = baseMapper.selectList(wrapper);
        return categories.stream().collect(Collectors.toMap(Category::getId, Category::getName));
    }

    @Override
    public Map<Integer, String> getIdNameMap(List<Integer> ids) {
        LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(Category::getId, Category::getName);
        wrapper.in(!CollectionUtils.isEmpty(ids), Category::getId, ids);
        List<Category> categories = baseMapper.selectList(wrapper);
        return categories.stream().collect(Collectors.toMap(Category::getId, Category::getName));
    }

    @Override
    public List<CategorySelectVo> getSelectVo_old(CategoryListDto dto) {

        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Category::getStatus, EStatus.on.getValue());
//        if (dto.getCategoryId() != null) {
//            queryWrapper.apply("FIND_IN_SET('" + dto.getCategoryId() + "', path)");
//        }
        List<Category> categories = baseMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(categories)) {
            return Collections.emptyList();
        }

        List<Category> top = categories.stream().filter(category -> {
            if (StrUtil.isEmpty(category.getSite())) {
                return false;
            }
            List<Integer> collect = Arrays.stream(category.getSite().split(",")).map(Integer::parseInt).toList();
            return collect.contains(ECategorySiteType.top.getValue());
        }).toList();
        List<Category> secondary = categories.stream().filter(category -> {
            if (StrUtil.isEmpty(category.getSite())) {
                return false;
            }
            List<Integer> collect = Arrays.stream(category.getSite().split(",")).map(Integer::parseInt).toList();
            return collect.contains(ECategorySiteType.secondary.getValue());
        }).toList();

        List<CategorySelectVo> categorySelectVos = new ArrayList<>();
        top.forEach(category -> {
            categorySelectVos.add(getCategorySelectVo(category.getName(), category.getId()));
        });
        BeanUtil.copyToList(top, CategorySelectVo.class, new CopyOptions());
        for (CategorySelectVo categorySelectVo : categorySelectVos) {
            for (Category category : secondary) {
                if (category.getPid().equals(categorySelectVo.getValue())) {
                    CategorySelectVo children = getCategorySelectVo(category.getName(), category.getId());
                    categorySelectVo.getChildren().add(children);
                }
            }
        }
        return categorySelectVos;
    }

    /**
     * 根据店铺id获取店铺新增商品时能够选择的品类
     *
     * @param dto
     */
    @Override
    public List<CategoryListVo> listChildCategory(IdDto dto) {
        LambdaQueryWrapper<Store> wrapper = Wrappers.lambdaQuery();
        wrapper.select(Store::getCategoryId).eq(Store::getId, dto.getId()).last(" limit 1");
        Store store = storeMapper.selectOne(wrapper);
        LambdaQueryWrapper<Category> cw = Wrappers.lambdaQuery();
        cw.select(Category::getId, Category::getName, Category::getCover).and(qw -> qw.eq(Category::getPid, store.getCategoryId())
                        .or().eq(Category::getId, store.getCategoryId()))
                .eq(Category::getIsShow, EShowStatus.show.getValue()).eq(Category::getStatus, EStatus.on.getValue())
                .orderByAsc(Category::getSort);
        List<Category> categories = baseMapper.selectList(cw);
        if (categories.isEmpty()) {
            return Collections.emptyList();
        }
        List<CategoryListVo> categoryListVos = BeanUtil.copyToList(categories, CategoryListVo.class);
        categoryListVos.forEach(c -> c.setCover(AppConfigUtil.getUrlPrefix(c.getCover())));
        return categoryListVos;
    }

    @Override
    @CacheEvict(cacheNames = "category", allEntries = true)
    public void test() {
    }

    /**
     * 后台查询品类列表
     *
     * @param dto
     */
    @Override
    public IPage<AdminCategoryPageVo> pageCategory(CategoryPageDto dto) {
        Page<Category> page = new Page<>(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<Category> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(Category::getPid, 0).orderByAsc(Category::getSort);

        //父级类目
        IPage<Category> categoryPage = baseMapper.selectPage(page, wrapper);
        List<Category> categories = categoryPage.getRecords();
        List<AdminCategoryPageVo> parent = BeanUtil.copyToList(categories, AdminCategoryPageVo.class);
        parent.forEach(category -> {
            category.setCoverUrl(AppConfigUtil.getUrlPrefix(category.getCover()));
        });

        //所有非父级
        List<Category> list = baseMapper.selectList(Wrappers.<Category>lambdaQuery()
                .ne(Category::getPid, 0));
        List<AdminCategoryPageVo> children = BeanUtil.copyToList(list, AdminCategoryPageVo.class);
        children.forEach(category -> {
            category.setCoverUrl(AppConfigUtil.getUrlPrefix(category.getCover()));
        });

        List<AdminCategoryPageVo> categoryPageVos = setChild(parent, children);

        IPage<AdminCategoryPageVo> voIPage = new Page<>();
        voIPage.setRecords(categoryPageVos);
        voIPage.setTotal(categoryPage.getTotal());
        voIPage.setPages(categoryPage.getPages());
        voIPage.setSize(categoryPage.getSize());
        return voIPage;
    }

    /**
     * 后台查询类目详情
     *
     * @param dto
     */
    @Override
    public AdminCategoryInfoVo getInfo(IdDto dto) {
        Category category = baseMapper.selectById(dto.getId());
        return BeanUtil.copyProperties(category, AdminCategoryInfoVo.class);
    }

    /**
     * 后台修改类目排序
     *
     * @param dto
     */
    @Override
    public boolean changeSort(CategoryChangeSortDto dto) {
        Category first = baseMapper.getPathById(dto.getFirstId());
        Category next = baseMapper.getPathById(dto.getNextId());
        if (!first.getPath().equals(next.getPath())) {
            throw new GeneralException(HttpStatus.onlyEqualPath);
        }
        Integer firstSort = first.getSort();

        Integer nextSort = next.getSort();

        baseMapper.updateSort(dto.getFirstId(), nextSort);

        baseMapper.updateSort(dto.getNextId(), firstSort);

        return true;
    }

    private CategorySelectVo getCategorySelectVo(String text, Integer value) {
        CategorySelectVo categorySelectVo = new CategorySelectVo();
        categorySelectVo.setValue(value);
        categorySelectVo.setText(text);
        return categorySelectVo;
    }


    /**
     * 设置子集
     *
     * @return
     */
    private List<AdminCategoryPageVo> setChild(List<AdminCategoryPageVo> parent, List<AdminCategoryPageVo> children) {
        if (CollectionUtil.isEmpty(parent)) {
            return Collections.emptyList();
        }
        for (AdminCategoryPageVo pageVo : parent) {
            List<AdminCategoryPageVo> voList = children.stream().filter(c -> c.getPid().equals(pageVo.getId())).toList();
            pageVo.setChildren(setChild(voList, children));
        }
        return parent;
    }


    @Override
    public List<CategoryListVo> categoryAll(List<Category> categories) {
        List<CategoryListVo> list = new ArrayList<>();
        if (!categories.isEmpty()) {
            categories.forEach(category -> {
                CategoryListVo vo = new CategoryListVo();
                vo.setId(category.getId());
                vo.setName(category.getName());
                vo.setCover(AppConfigUtil.getUrlPrefix(category.getCover()));
                list.add(vo);
            });
        }
        return list;
    }

}